package com.perfect.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.perfect.utils.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.toolkit.trace.Tag;
import org.apache.skywalking.apm.toolkit.trace.Tags;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/goods")
@RefreshScope
@Slf4j
public class TestController {

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @Value("${perfect}")
    private String aaValueAnnotation;

    @Value("${yunCloud.live.callBackUrl}")
    private String callBackUrl;

    @Value("${anqila}")
    private String anqila;

    @Value("${mysql}")
    private String mysql;

    @Autowired
    private RestTemplate restTemplate;

    private static final String resourceName = "sentinelHello";

    /**
     * 测试该接口被 通过nacos 服务注册的形式调用
     */
    @GetMapping("/nacos/discovery/test")
    public void nacosDiscoveryTest() {
        System.out.println("nacos 服务注册成功了");
    }

    /**
     * restTemplate @LoadBalance 组合调用服务
     */
    @RequestMapping("/test")
    @Trace
    public void testNacos() {
        System.out.println("test...................");
        restTemplate.getForEntity("http://order-service/nacos/resttemplate/loadbalance/annotation", null);
    }

    @SentinelResource(value = "/nacos/config/test",
    blockHandler = "blockHandler",blockHandlerClass = ExceptionUtils.class,
    fallback = "fallBack",fallbackClass = ExceptionUtils.class)
    @Trace
    @Tags({@Tag( key = "param",value ="arg[0]" ),@Tag( key="return", value="returnedObj")})
    @RequestMapping(value = "/nacos/config/test")
    public String configTest(HttpServletRequest request) {
        log.info("test");
        int localPort = request.getLocalPort();
        String parameter = request.getParameter("x-Request-x");
        log.info("x-Request-x="+parameter);
        String request_header = request.getHeader("x-Request_header");
        log.info("x-Request_header="+request_header);

        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String aa = environment.getProperty("aa");
        String out = String.format("读取nacos 配置 aa：%s,@Value 注解拉取配置：%s,callBackUrl=%s,系统当前时间=%s", aa, aaValueAnnotation, callBackUrl, System.currentTimeMillis());
        log.info(out);
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return out+"port="+localPort;
    }

    /**
     * 流控规则 缺点：业务代码侵入性强，需要针对每个control 写，和修改流控规则
     */
    @PostConstruct
    private static void initFlowRules() {
        List<FlowRule> ruleList = new ArrayList<>();

        FlowRule rule = new FlowRule();

        //受保护的资源名称
        rule.setResource(resourceName);

        //流控规则QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);

        //数量限制 限制 qps 2
        rule.setCount(2);

        ruleList.add(rule);
        FlowRuleManager.loadRules(ruleList);
    }

    /**
     * sentinel 测试接口
     */
    @RequestMapping(value = "/sentinel/test")
    public String sentinelTest() {


        Entry entry = null;
        try {
            // 资源名可以使用任意有业务意义名称，比如方法名、业务接口名等可以唯一标识的即可
            entry = SphU.entry(resourceName);

            //受保护的资源，业务代码
            System.out.println("sentinel hello");
        } catch (BlockException e) {

            //资源保护阻止， 被限流或降级的业务方法就在这里处理
            e.printStackTrace();
        } catch (Exception e) {
            //若配置了降级规则，需要通过这种方式记录业务异常
            e.printStackTrace();
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }

        return "sentenel";
    }

}
